/*
 * Copyright 2007 - The JDPF Project Team (http://www.jdpf.org) 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
package org.jdpf.core.kernel.elements;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.jdpf.core.kernel.exceptions.CoreException;
import org.jdpf.core.kernel.exceptions.CoreExceptionsEnum;

/**
 * Represents the net <em>Place</em>. A place can accept multiple tokens. 
 * The management of tokens is according to first in first out (FIFO) logic.
 * 
 * @author Paolo Ciccarese
 */
public class Place extends AbstractNetElement implements IWiringChecking {

	private static final String NO_ID = "no-id";
	private static final String NO_NAME = "no-name";
	
	/**
	 * The place  input arc list
	 */
	private List<OutputArc> inputs = new ArrayList<OutputArc>(10);
	
	/**
	 * The place output arc list
	 */
	private List<InputArc> outputs = new ArrayList<InputArc>(10);
	
	/**
	 * The list structure for managing the tokens (FIFO)
	 */
	private LinkedList<Token> tokens = new LinkedList<Token>();

	/**
	 * The list of the inspectors. Inspectors are created as subnets. This means 
	 * that they are considered as single entities and not as sum of arcs, place
	 * and transition as the other parts of the pipeline.
	 */
	private List<InspectorSubNet> inspectorList = new ArrayList<InspectorSubNet>();

	/**
	 * Constructor.
	 * @param id	The unique id of the place
	 * @param name	The name of the place
	 */
	public Place(String id, String name) {
		super(id, name);
	}
	
	/**
	 * Id constructor.
	 * @param id	Constructor with id
	 */
	public Place(String id){
		super(id, Place.NO_NAME);
	}
	
	/**
	 * Empty constructor
	 */
	public Place(){
		super(Place.NO_ID, Place.NO_NAME);
	}

	public void checkWiringValidity() throws CoreException {
		if (inputs.size() < 1)
			throw new CoreException(CoreExceptionsEnum.TooFewInputArcs, 
					buildMessage("Too few input arcs"));
		if (inputs.size() > 1)
			throw new CoreException(CoreExceptionsEnum.TooManyInputArcs, 
					buildMessage("Too many input arcs"));	
		if (outputs.size() < 1)
			throw new CoreException(CoreExceptionsEnum.TooFewOutputArcs, 
					buildMessage("Too few output arcs"));
		if (outputs.size() > 1)
			throw new CoreException(CoreExceptionsEnum.TooManyOutputArcs, 
					buildMessage("Too many output arcs"));		
	}
	
	private String buildMessage(String message) {
		StringBuilder sb = new StringBuilder();
		
		sb.append("Node with id: ").append(super.getId());
		sb.append(" and name: ").append(super.getName()).append("\n");
		
		sb.append("Message: ").append(message).append("!\n");
		
		sb.append("Min/Max input arcs allowed 1/1 - inputs: ");
		sb.append(inputs.size()).append("\n");
		
		sb.append("Min/Max output arcs allowed 1/1 - outputs: ");
		sb.append(outputs.size()).append("\n");
		
		return sb.toString();
	}	
	
	/**
	 * Add an input arc for the place.
	 * @param outputArc Output arc for the transition.
	 * @return The current place
	 */
	public Place addInputArc(OutputArc outputArc) {
		this.inputs.add(outputArc);
		return this;
	}

	/**
	 * Add an output arc for the place.
	 * @param inputArc Input arc for the transition
	 * @return The current place
	 */
	public Place addOutputArc(InputArc inputArc) {
		this.outputs.add(inputArc);
		return this;
	}

	/**
	 * Returns the list of input arcs for the place.
	 * @return List of input arcs.
	 */
	public List<OutputArc> getInputArcs() {
		return inputs;
	}

	/**
	 * Returns the list of output arcs for the place.
	 * @return List of output arcs.
	 */
	public List<InputArc> getOutputArcs() {
		return outputs;
	}

	/**
	 * Remove the specified arc from the input arcs list.
	 * @param outputArc Output arc for the transition.
	 * @return The current place
	 */
	public Place removeInputArc(OutputArc outputArc) {
		this.inputs.remove(outputArc);
		return this;
	}

	/**
	 * Remove the specified arc from the output arcs list
	 * @param inputArc Input arc for the transition.
	 * @return The current place
	 */
	public Place removeOutputArc(InputArc inputArc) {
		this.outputs.remove(inputArc);
		return this;
	}

	/**
	 * Adds a token to the place. 
	 * @param token	The new Token
	 */
	public void addToken(Token token) {
		this.tokens.addLast(token);
		for(InspectorSubNet inspector: inspectorList){
		    inspector.addToken(token);
		}
	}
	
	/**
	 * Removes the first token from the place.
	 * @return The token removed
	 */
	public Token removeToken() {
		if(this.tokens.size()>0){
			return this.tokens.removeFirst();
		}else{
			return null;
		}
	}
	
	/**
	 * Returns the first token in the place.
	 * @return	The token
	 */
	public Token getToken() {
		if(tokens.isEmpty()){
			return null;
		}else{
			return this.tokens.getFirst();
		}	
	}
	
	public List<Token> getTokens(){
		return tokens;
	}
	
	public boolean isStartPlace(){
	    return (this.getInputArcs().size()==0);
	}
	
	public boolean isEndPlace(){
	    return (this.getOutputArcs().size()==0);
	}

	public void addInspector(ITransition inspector){
	    inspectorList.add(new InspectorSubNet(inspector)); 
	}
		
	public List<ITransition> getInspectors(){
		List<ITransition> list = new ArrayList<ITransition>();
		for(InspectorSubNet subNet : inspectorList){
			list.add(subNet.getInspectorTransition());
		}
		
		return list;
	}
}
